#ifndef ASPRITE_H
#define ASPRITE_H

#include <Irrlicht.h>
#include <string>

using namespace irr;
using namespace core;
using namespace video;
using namespace io;

struct EXT_DRAW_INFO 
{
	float	REF_X,REF_Y;
	int	REF_POINT;
	float	BRIGHT_SCALE;//0 - 4
	bool	BRIGHT_HALO;
	bool	GRAY_EFFECT;
	SColor	BLEND_COLOR;
	EXT_DRAW_INFO();
};

#define ESCAPE_CHARACTER_PREFIX L'^'

const f32 SPRITE_SCALE_3D		= 1.0f/43.0f;

class ASprite
{
	// ASprite_Constants.hxx - phone specific constants
	////////////////////////////////////////////////////////////////////////////////////////////////////

	static const int MAX_SPRITE_PALETTES	= 16;
	static const int MAX_MODULE_MAPPINGS	= 16;
	static const int MAX_FLIP_COUNT			= 8;

	static const int TMP_BUFFER_SIZE		= 10 * 1024;
	static const int PNG_BUFFER_SIZE		= 10 * 1024;

public:

	////////////////////////////////////////////////////////////////////////////////////////////////////
	static const bool USE_HYPER_FM = true;				// use Hyper Frame Modules
	static const bool USE_INDEX_EX_FMODULES = true;		// true|false -> max. 1024|256 modules refs. from a FModule
	static const bool USE_INDEX_EX_AFRAMES = true;		// true|false -> max. 1024|256 frames refs. from an Anim

	//////////////////////////////////////////////////
private:
	static const short BSPRITE_v003	= (short)0x03DF; // supported version
	static const short BSPRITE_v004	= (short)0x04DF; // supported version
	static const short BSPRITE_v005	= (short)0x05DF; // supported version

	//////////////////////////////////////////////////
	// BSprite flags

	static const int BS_MODULES			= (1 << 0);
	static const int BS_MODULES_XY		= (1 << 1);
	static const int BS_MODULES_IMG		= (1 << 2);
	static const int BS_MODULES_WH_SHORT	= (1 << 4);		// export w, h for each module as short
	static const int BS_MODULES_XY_SHORT	= (1 << 5);		// export x, y for each module as short
	static const int BS_FRAMES			= (1 << 8);
	static const int BS_FM_OFF_SHORT	= (1 << 10);    // export fm offsets as shorts
	static const int BS_NFM_1_BYTE		= (1 << 11);    // export nfm as byte
	static const int BS_SKIP_FRAME_RC	= (1 << 12);    // do not export frame rect
	static const int BS_FRAME_COLL_RC	= (1 << 13);	// export collision rect
	static const int BS_FM_PALETTE		= (1 << 14);	// export palette used by the module
	static const int BS_FRAME_RECTS		= (1 << 15);	// export frame rects
	static const int BS_ANIMS			= (1 << 16);
	static const int BS_AF_OFF_SHORT	= (1 << 18);    // export af offsets as shorts
	static const int BS_NAF_1_BYTE		= (1 << 19);    // export naf as byte
	static const int BS_MODULE_IMAGES	= (1 << 24);
	static const int BS_PNG_CRC			= (1 << 25);
	static const int BS_KEEP_PAL		= (1 << 26);
	static const int BS_TRANSP_FIRST	= (1 << 27);
	static const int BS_TRANSP_LAST		= (1 << 28);
	static const int BS_SINGLE_IMAGE	= (1 << 29);
	static const int BS_MODULE_USAGE	= (1 << 30);

	static const int BS_DEFAULT_DOJA	= (BS_MODULES | BS_FRAMES | BS_ANIMS);
	static const int BS_DEFAULT_MIDP2	= (BS_MODULES | BS_FRAMES | BS_ANIMS | BS_MODULE_IMAGES);
	static const int BS_DEFAULT_NOKIA	= BS_DEFAULT_MIDP2;
	static const int BS_DEFAULT_MIDP1	= (BS_MODULES | BS_MODULES_XY | BS_FRAMES | BS_ANIMS);
	static const int BS_DEFAULT_MIDP1b	= (BS_MODULES | BS_FRAMES | BS_ANIMS | BS_MODULE_IMAGES | BS_PNG_CRC);
	static const int BS_DEFAULT_MIDP1c	= (BS_MODULES | BS_MODULES_XY | BS_FRAMES | BS_ANIMS | BS_SINGLE_IMAGE);

	static const int BS_FM_FREE_ROTATE_SCALE    = (1 << 0);
	//////////////////////////////////////////////////

	static const short PIXEL_FORMAT_8888		= (short)0x8888;
	static const short PIXEL_FORMAT_4444		= (short)0x4444;
	static const short PIXEL_FORMAT_1555		= (short)0x5515;
	static const short PIXEL_FORMAT_0565		= (short)0x6505;

	//////////////////////////////////////////////////

	static const short ENCODE_FORMAT_I2			= 0x0200;
	static const short ENCODE_FORMAT_I4			= 0x0400;
	//	const short ENCODE_FORMAT_I8			= 0x0800;
	static const short ENCODE_FORMAT_I16		= 0x1600;
	//	const short ENCODE_FORMAT_I16MP		= 0x16??;
	//	const short ENCODE_FORMAT_I32		= 0x3200;
	//	const short ENCODE_FORMAT_I64		= 0x6400;
	//	const short ENCODE_FORMAT_I128		= 0x2801;
	static const short ENCODE_FORMAT_I256		= 0x5602;
	//	const short ENCODE_FORMAT_I127_		= 0x2701;
	static const short ENCODE_FORMAT_I64RLE		= 0x64F0;
	static const short ENCODE_FORMAT_I127RLE	= 0x27F1;
	static const short ENCODE_FORMAT_I256RLE	= 0x56F2;
public:
	//////////////////////////////////////////////////
	// Frames/Anims flags...

	static const unsigned char FLAG_FLIP_X	= 0x01;
	static const unsigned char FLAG_FLIP_Y	= 0x02;
	static const unsigned char FLAG_ROT_90	= 0x04;
	static const unsigned char FLAG_FREE_ROT	= 0x08;

	static const unsigned char FLAG_USER0	= 0x10; // user flag 0
	static const unsigned char FLAG_USER1	= 0x20; // user flag 1

	static const unsigned char FLAG_HYPER_FM	= 0x10; // Hyper FModule, used by FModules

	// Index extension...
	static const int FLAG_INDEX_EX_MASK = 0xC0; // 11000000, bits 6, 7
	static const int INDEX_MASK			= 0x03FF; // max 1024 values
	static const int INDEX_EX_MASK		= 0x0300;
	static const int INDEX_EX_SHIFT 	= 2;

	//////////////////////////////////////////////////
	// flags passed as params...

	static const unsigned char FLAG_OFFSET_FM = 0x10;
	static const unsigned char FLAG_OFFSET_AF = 0x20;

	//////////////////////////////////////////////////

	static const int 	BOTTOM 	=	32;
	static const int 	HCENTER =	1;
	static const int 	LEFT 	=	4;
	static const int 	RIGHT 	=	8;
	static const int 	TOP 	=	16;
	static const int 	VCENTER =	2;

	//USE_EXTERN_IMAGE
public:

	void Load( char* rawdata);
	void LoadSprite(const char* spriteName);
	void LoadTexture(const char* textureName);

	////////////////////////////////////////////////////////////////////////////////////////////////////

	void BuildCacheImages(ITexture*	image);	

	////////////////////////////////////////////////////////////////////////////////////////////////////

	int GetAFrameTime(int anim, int aframe)
	{
		//	int af = (_anims_af_start[anim] + aframe);
		//	return _aframes[((af<<2) + af + 1)] & 0xFF;

		return _aframes[(_anims_af_start[anim] + aframe) * 5 + 1] & 0xFF;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	int GetAFrames(int anim)
	{
		return _anims_naf[anim]&0xFF;
		//	return _anims_naf[anim]&0xFFFF;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	int GetFModules(int frame)
	{
		return _frames_nfm[frame]&0xFF;
		//	return _frames_nfm[frame]&0xFFFF;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	int GetModuleWidth(int module)
	{
		return _modules_w[module]&0xFF;
	}

	int GetModuleHeight(int module)
	{
		return _modules_h[module]&0xFF;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	short GetFrameLeft(int frame)
	{
		if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
			return (_frames_rc[(frame<<3) + 0] & 0xFF) | (_frames_rc[(frame<<3) + 1] << 8);
		else
			return _frames_rc[(frame<<2) + 0];
	}

	short GetFrameTop(int frame)
	{
		if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
			return (_frames_rc[(frame<<3) + 2] & 0xFF) | (_frames_rc[(frame<<3) + 3] << 8);
		else
			return _frames_rc[(frame<<2) + 1];
	}
    int GetFontFrameLeft(int frame);
    int GetFontFrameWidth(int frame);
	int GetFrameWidth(int frame)
	{
		if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
			return (_frames_rc[(frame<<3) + 4] & 0xFF) | (_frames_rc[(frame<<3) + 5] << 8);
		else
			return _frames_rc[(frame<<2) + 2]&0xFF;
	}

	int GetFrameHeight(int frame)
	{
		if ((bs_flags & BS_FM_OFF_SHORT) != 0) 
			return (_frames_rc[(frame<<3) + 6] & 0xFF) | (_frames_rc[(frame<<3) + 7] << 8);
		else
			return _frames_rc[(frame<<2) + 3]&0xFF;
	}



    int GetFrameColLeft(int frame)
    {
        return _frames_col[frame*4 + 0];
    }

    int GetFrameColTop(int frame)
    {
        return _frames_col[frame*4 + 1];
    }
    int GetFrameColWidth(int frame)
    {
        return _frames_col[frame*4 + 2]&0xFF;
    }

    int GetFrameColHeight(int frame)
    {
        return _frames_col[frame*4 + 3]&0xFF;
    }

	////////////////////////////////////////////////////////////////////////////////////////////////////

	int GetFrames()
	{
		return _nFrames;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	int GetAnimFrame(int anim, int aframe)
	{

		int off = (_anims_af_start[anim] + aframe) * 5;
		return _aframes[off]&0xFF;
		//USE_SINGLE_ARRAY_FOR_FM_AF
		//USE_SINGLE_ARRAY_FOR_FM_AF
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	int GetFModuleOffX(int frame, int fmodule);
	int GetFModuleOffY(int frame, int fmodule);

	////////////////////////////////////////////////////////////////////////////////////////////////////

	void PaintAFrame(int anim, int aframe, int posX, int posY, int flags, int hx, int hy,int alpha = 255,EXT_DRAW_INFO* info = NULL);

	////////////////////////////////////////////////////////////////////////////////////////////////////

	void PaintFrame(int frame, int posX, int posY, int flags, int hx, int hy,int alpha = 255 ,int rotate = 0,int xscale = 100,int yscale = 100,EXT_DRAW_INFO* info = NULL)
	{
		//	System.out.println("PaintFrame(g, "+frame+", "+posX+", "+posY+", 0x"+Integer.toHexString(flags)+", "+hx+", "+hy+")");
		int nFModules = _frames_nfm[frame]&0xFF;

		//	int nFModules = _frames_nfm[frame]&0xFFFF;
		for (int fmodule = 0; fmodule < nFModules; fmodule++)
			PaintFModule(/*g,*/ frame, fmodule, posX, posY, flags, hx, hy,alpha,rotate,xscale,yscale,info);
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	void PaintFModule(int frame, int fmodule, int posX, int posY, int flags, int hx, int hy,int alpha = 255, int rotate = 0,int xscale = 100,int yscale = 100,EXT_DRAW_INFO* info = NULL);
	
	////////////////////////////////////////////////////////////////////////////////////////////////////

	void PaintModule(/*Graphics g,*/ int module, int posX, int posY, int flags, int rotate = 0,int xscale = 100,int yscale = 100 ,int alpha = 255,EXT_DRAW_INFO* info = NULL);

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 3d stuff

	void PaintAFrame3D(int anim, int aframe, const core::vector3df& pos, int flags, f32 hx = 0.0f, f32 hy = 0.0f, f32 xscale = SPRITE_SCALE_3D, f32 yscale = SPRITE_SCALE_3D);
	void PaintFrame3D(int frame, const core::vector3df& pos, int flags, f32 hx = 0.0f, f32 hy = 0.0f, f32 xscale = SPRITE_SCALE_3D, f32 yscale = SPRITE_SCALE_3D);
	void PaintFModule3D(int frame, int fmodule, const core::vector3df& pos, int flags, f32 hx = 0.0f, f32 hy = 0.0f, f32 xscale = SPRITE_SCALE_3D, f32 yscale = SPRITE_SCALE_3D);
	void PaintModule3D(int module, const core::vector3df& pos, int flags, f32 hx = 0.0f, f32 hy = 0.0f, f32 xscale = SPRITE_SCALE_3D, f32 yscale = SPRITE_SCALE_3D);

	////////////////////////////////////////////////////////////////////////////////////////////////////
public:
	void SetCurrentPalette(int pal)		{ /*if (pal < _palettes)*/ _crt_pal = pal; }
	int GetCurrentPalette()				{ return _crt_pal; }

	////////////////////////////////////////////////////////////////////////////////////////////////////

	/*
	void SetColor(int index, short color)
	{
	//	_font_color =
	_pal[_crt_pal][index] = color;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	void SetColor(int index, int color)
	{
	//	_font_color =
	_pal[_crt_pal][index] = (short)(((((color & 0xFF000000)>>24) & 0xF0)<<8) |
	((((color & 0x00FF0000)>>16) & 0xF0)<<4) |
	((((color & 0x0000FF00)>>8 ) & 0xF0)   ) |
	((((color & 0x000000FF)    ) & 0xF0)>>4));
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	void SetColor(int index, int a, int r, int g, int b)
	{
	//	_font_color =
	_pal[_crt_pal][index] = (short)(((a & 0xF0)<<8) |
	((r & 0xF0)<<4) |
	((g & 0xF0)   ) |
	((b & 0xF0)>>4));
	}
	*/

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// Palette generation based on other palette...

	// -1 - original colors
	//  0 - invisible (the sprite will be hidden)
	//  1 - red-yellow
	//  2 - blue-cyan
	//  3 - green
	//  4 - grey

/*
	static int[] GenPalette(int type, int pal[])
	{
		if (type <  0) return pal;	// original
		if (type == 0) return null; // invisible

		int[] new_pal = new int[pal.length];
		switch (type)
		{
			/ *			case 1: // red - yellow
			for (int i = 0; i < pal.length; i++)
			new_pal[i] = (pal[i] | 0x00FF3300) & 0xFFFFFF00;
			break;

			case 2: // blue - cyan
			for (int i = 0; i < pal.length; i++)
			new_pal[i] = (pal[i] | 0x000033FF) & 0xFF00FFFF;
			break;

			case 3: // green
			for (int i = 0; i < pal.length; i++)
			new_pal[i] = (pal[i] | 0x00000000) & 0xFF00FF00;
			break;
			* /
		case 4: // grey (desaturate)
			for (int i = 0; i < pal.length; i++)
			{
				int a = (pal[i] & 0xFF000000);
				int r = (pal[i] & 0x00FF0000) >> 16;
				int g = (pal[i] & 0x0000FF00) >> 8;
				int b = (pal[i] & 0x000000FF);
				int l = ((r + b + g) / 3) & 0x000000FF;
				new_pal[i] = ((l << 16) | (l << 8) | l | a);
			}
			break;
			/ *
			case 5: // blend with black 50%
			for (int i = 0; i < pal.length; i++)
			{
			int a = (pal[i] & 0xFF000000);
			int r = (pal[i] & 0x00FC0000) >> 2;
			int g = (pal[i] & 0x0000FC00) >> 2;
			int b = (pal[i] & 0x000000FC) >> 2;
			new_pal[i] = (a | r | g | b);
			}
			break;
			* /		}

		return new_pal;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////

	void ModifyPaletteAlpha (int palNb, short alpha)
	{
		alpha = (short)((alpha<<4)/100);

		for (int i=0;i<_pal[palNb].length;i++)
		{
			if ((_pal[palNb][i]&0x0fff) != 0x0f0f)               
				_pal[palNb][i] = (short)( ( (alpha&0xf) << 12) | (_pal[palNb][i] &0x0fff));
		}
	}
*/

	////////////////////////////////////////////////////////////////////////////////////////////////////

// 	static int  temp[] = new int [TMP_BUFFER_SIZE];

	//////////////////////////////////////////////////

	// Modules...
	int			_nModules;			// number of modules
	short*		_modules_x;			// x for each module [BS_MODULES_XY_SHORT]
	short*		_modules_y;			// y for each module [BS_MODULES_XY_SHORT]
	short*		_modules_w;			// width for each module
	short*		_modules_h;			// height for each module

	// Frames...
	int			_nFrames;			// number of frames
	short*		_frames_nfm;		// number of FModules (max 256 FModules/Frame)
	//	short[]		_frames_nfm;		// number of FModules (max 65536 FModules/Frame)
	short*		_frames_fm_start;	// index of the first FModule
	char*		_frames_rc;			// frame bound rect (x y width height)
	char*		_frames_col;		// collision rect for each frame (x y width height)
	// FModules...

	int nFModules;
	char*		_nFModules;				// 4 bytes for each FModule
	//int	___nFrames;
	//USE_SINGLE_ARRAY_FOR_FM_AF
	//USE_SINGLE_ARRAY_FOR_FM_AF

	//byte*		_fmodules_id;		// 0 : module index
	//short*		_fmodules_ox;			// 1 : ox
	//short*		_fmodules_oy;			// 2 : oy
	//byte*		_fmodules_flags;		// 3 : flags

	// Anims...
	short*		_anims_naf;			// number of AFrames (max 256 AFrames/Anim)
	//	short[]		_anims_naf;			// number of AFrames (max 65536 AFrames/Anim)
	short*		_anims_af_start;	// index of the first AFrame

	// AFrames...

	int	_nAFrames;
	char*		_aframes;			// 5 bytes for each AFrame
	//USE_SINGLE_ARRAY_FOR_FM_AF
	//USE_SINGLE_ARRAY_FOR_FM_AF

	//	byte[]		_aframes_frame;			// 0 : frame index
	//	byte[]		_aframes_time;			// 1 : time
	//	byte[]		_aframes_ox;			// 2 : ox
	//	byte[]		_aframes_oy;			// 3 : oy
	//	byte[]		_aframes_flags;			// 4 : flags
	int	_nAnims;

	// Module mappings...
// 	int[][]		_map; 				// all mappings
// 	//	int			_mappings;			// number of mapings
// 	private int	_cur_map;			// current mapping

	// Palettes...
	//	short 		_pixel_format;		// always converted to 8888
	int			bs_flags;			// sprite format
	int			ex_bs_flags;			// sprite format

// 	int  [][]	_pal; 				// all palettes

// 	byte[][]	_transp;			// all transparences (A)
// 	byte[]		_pal_data; 			// palettes data for BS_DEFAULT_MIDP1c
// 	int			_palettes;			// number of palettes
// 	int			_colors;			// number of colors
	int _crt_pal;			// current palette
// 	bool		_alpha;				// has transparency ?
	//	int			_flags;				// transparency, etc.

	// Graphics data (for each module)...
	//	Image[]		_main_image;		// an image with all modules, for each palette
// 	short 		_data_format;
// 	int			_i64rle_color_mask;	// used by ENCODE_FORMAT_I64RLE
// 	int			_i64rle_color_bits;	// used by ENCODE_FORMAT_I64RLE
// 	byte[]		_modules_data;		// encoded image data for all modules
// 	short[]		_modules_data_off;	// offset for the image data of each module
	//	int[][]		_modules_data2;		// cashe image data (decoded)

	//USE_DYNAMIC_PNG

/*
	Image [][]	_modules_image;		// cache image for each module / with each palette

	//USE_NOKIA_UI

	//	Image[]		_modules_image_fx;	// cache image for each module (flipped horizontally)
	Image[]		_main_image;		// an image with all modules, for each palette
*/
	//ResTex3d*	_modules_image;
	ITexture*	_modules_image;
	// CRC data for creating a valid PNG stream...
// 	int[] _PNG_packed_PLTE_CRC;		// for each palette
// 	int[] _PNG_packed_tRNS_CRC;		// for each palette
// 	int[] _PNG_packed_IHDR_CRC;		// for each module
// 	int[] _PNG_packed_IDAT_ADLER;	// for each module
// 	int[] _PNG_packed_IDAT_CRC;		// for each module

// 	static int			_images_count;
// 	static int			_images_size;

	//3d stuff
	u16 indices[6];
	video::SMaterial* _material;
	video::S3DVertex vertices[4];
	float _fmodules_correction;

	////////////////////////////////////////////////////////////////////////////////////////////////////

	ASprite();
	~ASprite();
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ASprite debug constants

	static const bool DEBUG_bDbgS			= false;
	static const bool DEBUG_bErr				= false;
	static const bool DEBUG_bDbgVM			= false;
	static const bool DEBUG_bDbgO			= false;
	static const bool DEBUG_bEmu				= false;
	static const bool DEBUG_bDbgI			= false;
	static const bool DEBUG_bDbgModuleUsage	= false;

	////////////////////////////////////////////////////////////////////////////////////////////////////


	////////////////////////////////////////////////////////////////////////////////////////////////////

}; // class ASprite
#endif